package com.liwy3.lark;

import com.liwy3.*;
import com.liwy3.lark.msgbot.LarkBotMsg;
import com.liwy3.lark.msgbot.LarkBotMsgDiv;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.net.Inet4Address;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * https://open.feishu.cn/tool/cardbuilder?from=custom_bot_doc
 */
@Slf4j
public final class LarkMsgBot {
    /**
     * 最大消息长度
     */
    public static final int MSG_LENGTH = 256;

    private static ConcurrentHashMap<String, AtomicInteger> map = new ConcurrentHashMap();

    private String webhook;
    private String key;
    private int threshold = 10; // 每分钟

    public LarkMsgBot(String webhook, String key) {
        this.webhook = webhook;
        this.key = key;
    }

    public LarkMsgBot(String webhook) {
        this(webhook, RandomStringUtils.randomAlphabetic(64));
    }

    public LarkMsgBot setThreshold(int threshold) {
        this.threshold = threshold;
        return this;
    }

    /**
     * 发送root异常信息
     *
     * @param title 错误信息标题
     * @param ex    Exception信息
     */
    public void sendRootCause(String title, Throwable ex) {
        send(title, ExceptionUtils.getStackTrace(ExceptionUtils.getRootCause(ex)));
    }

    /**
     * 发送异常信息
     *
     * @param title 错误信息标题
     * @param ex    Exception信息
     */
    public void send(String title, Throwable ex) {
        send(title, ExceptionUtils.getStackTrace(ex));
    }

    public void send(String title, List<String> msgs) {
        LarkBotMsg msg = new LarkBotMsg();
        msg.getCard().setHeader(title, "red");
        LarkBotMsgDiv div = new LarkBotMsgDiv().addLarkMd(false, StringUtils.join(msgs, "\n"));
        msg.getCard().getElements().add(div);
        YQHttp.Response json = YQHttp.newRequest().setUrl(webhook).setPostBody(YQJsonUtils.toJson(msg)).json();
        log.info("发送机器人消息结果：{}", json.getContent());
    }

    public void send(String... msgs) {
        if (StringUtils.isNotBlank(webhook) && msgs.length > 0 && threshold(key)) {
            String host = YQException.ignore(() -> Inet4Address.getLocalHost().getHostName());

            LarkBotMsg msg = new LarkBotMsg();
            if (msgs.length > 1) { // 消息长度大于1才会设置标题, 最大长度 64 个字符
                msg.getCard().setHeader(StringUtils.substring(msgs[0], 0, 64), "red");
                msgs = Arrays.copyOfRange(msgs, 1, msgs.length);
            }

            String content = StringUtils.substring(StringUtils.join(msgs, "\n"), 0, 20480);
            if (content.length() > 1536) { // 内容过长时则改为文本发送
                String header = "服务器: " + host + "."  + "\n";
                header += "=============================\n\n";
                YQMap map = new YQMap()
                        .append("msg_type", "text")
                        .append("content", new YQMap().append("text", header + msgs[0] + "\n" + content));
                YQHttp.Response json = YQHttp.newRequest().setUrl(webhook).setPostBody(YQJsonUtils.toJson(map)).json();
                log.info("发送机器人消息结果：{}", json.getContent());
            } else {
                LarkBotMsgDiv div = new LarkBotMsgDiv()
                        .addLarkMd(true, "**服务器**\n" + host)
                        .addLarkMd(false, "")
                        .addLarkMd(false, "**消息内容**\n" + content);

                msg.getCard().getElements().add(div);
                YQHttp.Response json = YQHttp.newRequest().setUrl(webhook).setPostBody(YQJsonUtils.toJson(msg)).json();
                log.info("发送机器人消息结果：{}", json.getContent());
            }
        }
    }

    private boolean threshold(String key) {
        String thresholdKey = key + "_" + DateFormatUtils.format(new Date(), "yyyyMMddHHmm");
        AtomicInteger atomic = map.get(thresholdKey);
        if (atomic == null) {
            atomic = map.putIfAbsent(thresholdKey, new AtomicInteger());
            if (atomic == null) {
                atomic = map.get(thresholdKey);
            }
        }
        Iterator<Map.Entry<String, AtomicInteger>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, AtomicInteger> next = iterator.next();
            if (next.getKey().startsWith(key) && next.getKey().compareTo(thresholdKey) < 0) {
                iterator.remove();
            }
        }
        return atomic.addAndGet(1) <= threshold;
    }

}
